package com.colin.util.util;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;

/**
 * aes加密
 */
public class EncryptUtils {

    /**=================================================================*/
    /**                       前后端加密解密                               */
    /**=================================================================*/

    /**
     * key 必须为16位字符
     */
    public static final String KEY = "1234567890123456";

    /**
     * 加密
     *
     * @param pwd 待加密字符串
     * @param key 加密密钥
     * @return 加密串
     */
    public static String encrypt(String pwd, String key) throws Exception {
        byte[] byteContent = pwd.getBytes("UTF-8");
        //注意，为了能与 iOS 统一
        //这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
        byte[] enCodeFormat = key.getBytes();

        SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
        byte[] initParam = key.getBytes();
        IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
        // 指定加密的算法、工作模式和填充方式
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] encryptedBytes = cipher.doFinal(byteContent);
        // 同样对加密后数据进行 base64 编码
        byte[] encodeBytes = Base64.getEncoder().encode(encryptedBytes);
        String encode = new String(encodeBytes, StandardCharsets.UTF_8);
        // 过滤换行
        return encode.replaceAll("(\\r\\n|\\r|\\n|\\n\\r)", "");
    }

    /**
     * 解密
     *
     * @param encrypt 待解密字符串
     * @param key     加密密钥
     * @return 解密后的字符串
     */
    public static String decrypt(String encrypt, String key) throws Exception {
        byte[] encryptedBytes = Base64.getDecoder().decode(encrypt);
        byte[] enCodeFormat = key.getBytes();
        SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES");
        byte[] initParam = key.getBytes();
        IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
        byte[] result = cipher.doFinal(encryptedBytes);
        return new String(result, StandardCharsets.UTF_8);
    }

    public static String gernerateKey() {
        return gernerateKey(null);
    }

    public static String gernerateKey(String yyyyMMdd) {
        if (StringUtils.isBlank(yyyyMMdd)) {
            yyyyMMdd = DateUtil.getCurDateYYYYMMDD();
        }
        return getGUIDByDate(yyyyMMdd);
    }

    /**
     * 示例：20190826
     * 算法描述
     * 1、密钥头尾固定
     * 头：8
     * 尾：7
     * 2、将剩余的14位分为2部分获取
     * 第一部分，前8位
     * 第二部分，后6位
     * 3、循环值定义：
     * 从0到13进行自增，循环值即自增后的值
     * 4、每部分字符值获取
     * 第一部分：
     * 自左向右取日期的每一位作为值
     * 第二部分：
     * 循环值对日期长度取余，获取对应位置的日期值加日期最后一位的值作为值
     * 5、采用ASCII 字符表获取字符
     * 例如：若第四步获取到的值位2，则65+2=67，对应字符C（大写英文字符C）
     */
    private static String getGUIDByDate(String date) {
        StringBuilder key = new StringBuilder();
        String[] v = date.split("");

        //处理为int数组
        int[] a = new int[v.length];
        for (int i = 0; i < a.length; i++) {
            a[i] = Integer.parseInt(v[i]);
        }

        key.append("8");

        //生成14位字符串
        for (int i = 0; i < 14; i++) {
            int b = 0;
            if (i < a.length) {
                b = a[i];
            } else {
                b = a[i % a.length] + a[a.length - 1];
            }
            key.append((char) (b + 65));
        }

        key.append("7");

        return key.toString();
    }

    /**
     * 示例：2.2.1
     * 算法描述
     * 1、密钥头尾固定
     * 头：8
     * 尾：7
     * 2、将版本号拆分为4部分
     * 第一部分为第一位2
     * 第二部分为第二位2
     * 第三部分为第三位1
     * 第四部分为三位之和进行取模，（2+2+1）%3=2，则此部分为2
     * 3、循环值定义：
     * 从0到13进行自增，循环值即自增后的值
     * 4、每部分字符值获取
     * 第一部分：
     * 用第一位（2）+循环值
     * 第二部分：
     * 用第二位（2）+循环值-1
     * 第三部分：
     * 用第三位（1）+循环值+2
     * 第四部分：
     * 用取余之后的值（2）+循环值-3
     * 5、采用ASCII 字符表获取字符
     * 例如：若第四部获取到的值位2，则65+2=67，对应字符C（大写英文字符C）
     */
    private static String getGUIDByVersion(String version) {
        StringBuilder key = new StringBuilder();
        String[] v = version.split("\\.");

        //处理为int数组
        int[] a = new int[v.length];
        for (int i = 0; i < a.length; i++) {
            a[i] = Integer.parseInt(v[i]);
        }

        key.append("8");

        //根据版本号生成14位字符串
        int i = 0;
        while (i < 14) {
            i++;
            int b = 0;
            try {
                if (i < 5) {
                    b = a[0] + i;
                } else if (i >= 5 && i < 9) {
                    b = a[1] + i - 1;
                } else if (i >= 9 && i < 13) {
                    b = a[2] + i + 2;
                } else {
                    b = (a[0] + a[1] + a[2]) % 3 + i - 3;
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            key.append((char) (b + 65));
        }

        key.append("7");

        return key.toString();
    }

    /**=================================================================*/
    /**                       公，私钥加密解密                             */
    /**
     * =================================================================
     */

    private static String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIVH9vv1uAXC63Dmem1J4YTSQXGXWXGBNLtSJd6QDA/2Mhknozt/hVN9IXniUL5kqrZLRrBHCDqG0NSpopTtPQgDM+EUYG253fTNZdY1KPt/E0Jag6OhBzQLIfUBhZhkwI9/0JATv4b1PlLhPlaYQsLjwjzNrteS+Jb5IXD0mOifAgMBAAECgYAfxjBTkRj2BqoqmEfHlj11dMq1Uee475yqq8LrdFvr0j3PUWvQ223loYdBj3rAaS9pljib/dMwYMOYb3ibGJPb5N/kTWYrAyrw8B1w3+pqwCVCvwyyO1GHeifpdipZGor8dxEvzo5y4uZW8bdSiNL94xR7u+sCGsHU6Wph6FiWwQJBAMXLy3PkguKcZvh4+vWafNUqm4uNMFO72+v7KKwj67p+CHZkuU0OoRWwYwzAbkwo0Zl2eso/8m1ILb5ojNM1LEsCQQCsgC8Z0vKvDESjiNNtP+8WtJC3YJrBw1442nM6+NJ3mIhQX8xLPeqzfcii1zwOJA6NgFctIym9J05vjycy6dh9AkBhLeKU2xWvTtaMh6wNVUy10sw/QSwYR7kidPCjurZLlzaDLLOZ93EPXe0cS8MlKfzo4Dwt+EMcjBTlcyqVbo+HAkBASWVEUoFvCztpVsy0omcQJyrnrHEAVTcAKSlYiPKm74mdqpYQVHrrea0UQvJ2sqlZwsK6Z6hefg+VM9x/9PZhAkEAwdENVzlaaErjxbrECG7zsplOycojV6L7lRK5huzI2LkCTVmtXpEbtAJ6kbJJNwcMxFyvPkHFoP3cFPK8yTzbSQ==";
    private static String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCFR/b79bgFwutw5nptSeGE0kFxl1lxgTS7UiXekAwP9jIZJ6M7f4VTfSF54lC+ZKq2S0awRwg6htDUqaKU7T0IAzPhFGBtud30zWXWNSj7fxNCWoOjoQc0CyH1AYWYZMCPf9CQE7+G9T5S4T5WmELC48I8za7XkviW+SFw9JjonwIDAQAB";

    /**
     * 加密
     */
    public static String keyEncrypt(String content, String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] result = cipher.doFinal(content.getBytes());
        return Base64.getEncoder().encodeToString(result);
    }

    /**
     * 解密
     */
    public static String keyDecrypt(String ciphertext, String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey)));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, key);
        return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
    }

    public static void main(String[] args) {
//		String key = gernerateKey();
//		try {
//			String encr = encrypt("123", key);
//			System.out.println(encr);
//			System.out.println(decrypt(encr, key));
//		} catch (Exception e) {
//			e.printStackTrace();
//		}

        try {
            //原文
            Map<String, Object> info = new HashMap<>();
            info.put("n", "北京易盘点科技有限公司");
            info.put("exp", "2021-08-20 00:00:00");
            info.put("aa", 1000);
            String content = JSONObject.toJSONString(info);
            System.out.println("content: " + content);

            // 使用私钥加密
            String ciphertext = keyEncrypt(content, PRIVATE_KEY);
            System.out.println("ciphertext: " + ciphertext);
            // 使用公钥解密
            String text = keyDecrypt(ciphertext, PUBLIC_KEY);
            System.out.println("text: " + text);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
